/**
 * Repository is the class implementing a repository.
 * 
 *  A repository is used to group Modules objects.
 *  
 * @author Eric LEON
 * @version 1.0.0
 */
package models;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.ManyToMany;
import javax.persistence.OrderBy;

import play.db.jpa.Model;

import com.google.gson.annotations.Expose;

@Entity(name="tdRepository")
public class Repository extends Model {

	@Column(nullable=false,unique=true)
	@Expose
    public String name = null;
	
    @ManyToMany
    @OrderBy("name asc")
    public Set<Module> modules = new HashSet<Module>();

	@Override
	public String toString() {
		return this.name.toString();
	}

	/**
	 * Lists all created repositories, ordered by ascending name.
	 * @return	The list. 
	 */
    public static List<Repository> list() {
    	return Repository.find("order by name asc").fetch();
    }

	/**
	 * Lists all attached modules for this repository, ordered by ascending name.
	 * @return	The list. 
	 */
    public static List<Module> listModules(String name) {
    	return Repository.find("SELECT modules FROM models.Repository r WHERE r.name = ? ORDER BY name ASC", name).fetch();
    }
    
    /**
     * Attach a new module to this repository. Module cannot be attached twice.
     * @param mod	The module to attach.
     */
    public void attachModule(Module mod) {
		modules.add(mod);
    }
    
    /**
     * Detach the given module from this repository.
     * @param mod	The module to detach.
     */
    public void detachModule(Module mod) {
		this.modules.remove(mod);
    }
    
    /**
     * Returns the module of the given name.
     * @param name	The name of the module. 
     * @return	The module object found, or null if no module exists with the given name.
     */
    public Module getModule(String name) {
		for (Module m:modules) {
			if (m.name.equals(name)) {
				return m;
			}
		}
		return null;
    }
    
    /**
     * Builds the list of all modules not yet attached to this repository.
     * @return	The modules list.
     */
    public List<Module> availableModules() {
    	
    	if (id != null) {
	    	StringBuilder request = new StringBuilder();
	    	// Build the SQL request: find all modules that are not yet in the repository modules collection
	    	request.append("SELECT m FROM models.Module m, models.Repository r")
	    			.append(" WHERE r.id=?")
	    			.append(" AND m NOT IN ELEMENTS(r.modules)")
	    			.append(" ORDER BY m.name ASC");
	    	// Do the request
	    	return Module.find(request.toString(), this.id).fetch();
    	} else {
    		// Repository not yet persisted: return the entire modules list
    		return Module.list();
    	}
    }
    
    /**
     * Remove a module from all its repositories.
     * @param mod	The module object that must be detached. 
     */
    public static void removeModule(Module mod) {
    	
    	StringBuilder request = new StringBuilder();
    	List<Repository> rep;
    	// Build the SQL request: find all repositories containing module mod
    	request.append("SELECT r FROM models.Repository r, models.Module m")
    			.append(" WHERE m.id=?")
    			.append(" AND m IN ELEMENTS(r.modules)");
    	// Now detach module mod from all these repositories 
    	rep = Repository.find(request.toString(), mod.id).fetch();
    	for (Repository r:rep) {
    		// Parse all found repositories to detach the module
    		r.detachModule(mod);
    		// Commit change
    		r.save();
    	}
    }
}
